use std::collections::HashMap;

use anyhow::Ok;
use chrono::{offset, Duration, Utc};
use datasource_connector::connector::SimpleDataSourceConnector;
use model_graph_types::{
    container::sheduler::Scheduler,
    datasource::{AttributeConditionBuilder, DataSourceConnector, QueryConditionBuilder},
    modeling::{scheduler::SchedulerTask, system_meta_database, ClassGetter, ComparsionOperator},
};

use super::{start, SimpleJob};

pub struct SimpleScheduler {
    pub loaded: bool,
    pub current_queue: HashMap<String, SimpleJob>,
    pub wait_queue: HashMap<String, SimpleJob>,
    // pub(crate) worker: Option<JoinHandle<()>>,
}

impl SimpleScheduler {
    pub(crate) fn new() -> Self {
        Self {
            loaded: false,
            current_queue: HashMap::new(),
            wait_queue: HashMap::new(),
        }
    }

    pub async fn init(&mut self) -> anyhow::Result<()> {
        // 加载配置文件
        self.load_settings().await?;

        Ok(())
    }

    async fn load_settings(&mut self) -> anyhow::Result<()> {
        Ok(())
    }

    pub fn tick(&mut self) {
        for (key, mut job) in &mut self.current_queue {
            job.tick();
        }
    }

    pub fn time_till_next_job(&self) -> std::time::Duration {
        if self.current_queue.is_empty() {
            // Take a guess if there are no jobs.
            return std::time::Duration::from_millis(500);
        }
        let mut duration = Duration::zero();
        let now = Utc::now();
        for (_, job) in self.current_queue.iter() {
            for event in job.schedule.upcoming(offset::Utc).take(1) {
                let d = event - now;
                if duration.is_zero() || d < duration {
                    duration = d;
                }
            }
        }
        duration.to_std().unwrap()
    }
}

#[async_trait::async_trait]
impl Scheduler for SimpleScheduler {
    async fn load_tasks(&mut self) -> anyhow::Result<()> {
        self.reload_tasks().await?;
        Ok(())
    }

    async fn reload_tasks(&mut self) -> anyhow::Result<()> {
        let dsc = SimpleDataSourceConnector::default();

        let data = dsc
            .instance_query(
                &Some(system_meta_database()?),
                &system_meta_database()?,
                &SchedulerTask::get_identifier()?,
                QueryConditionBuilder::default()
                    .attributes(vec![AttributeConditionBuilder::default()
                        .name("enable")
                        .operator(ComparsionOperator::Empty)
                        .value("true")
                        .build()?])
                    .build()?,
                Some(SchedulerTask::common_query_extention()?),
                None,
            )
            .await?;

        tracing::debug!("{:?}", data.data);
        for value in data.data {
            let task: SchedulerTask = value.try_into()?;
            self.push_task(task).await?;
        }
        //循环
        Ok(())
    }

    async fn push_task(&mut self, task: SchedulerTask) -> anyhow::Result<()> {
        tracing::debug!("push_task:{:?}", task);
        start()?;
        //校验时间范围
        // 查询是否已经存在该任务
        let identifier = task.identifier.clone();

        if self.wait_queue.contains_key(&identifier) {
            //从等待队列移出到当前队列
            self.wait_queue.remove(&identifier);
            self.current_queue
                .insert(task.identifier.clone(), SimpleJob::new(task)?);
        } else if self.current_queue.contains_key(&identifier) {
            //已经存在，覆盖
            //停止原有的job
            self.current_queue.remove(&identifier);
            self.current_queue
                .insert(task.identifier.clone(), SimpleJob::new(task)?);
        } else {
            //都不存在，直接插入
            self.current_queue
                .insert(task.identifier.clone(), SimpleJob::new(task)?);
        }

        Ok(())
    }

    async fn tasks(&mut self, current: bool) -> anyhow::Result<Vec<SchedulerTask>> {
        Err(anyhow::anyhow!(""))
    }

    async fn stop_task(&mut self, task_identifier: &String) -> anyhow::Result<()> {
        Err(anyhow::anyhow!(""))
    }
}
